/*******************************************************************

    Author: Xiao
    Date: 2022/4/8 10:50

*******************************************************************/
package controller

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	rand2 "math/rand"
	"mcr/pkg/config"
	"mcr/pkg/dao"
	"mcr/pkg/logs"
	"mcr/pkg/middleware"
	"mcr/pkg/utils"
	socket "mcr/pkg/websocket"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type sendReq struct {
	BasicReq
	Where    string      `json:"where" binding:"required"`
	To       int         `json:"to" binding:"required"`
	Type     string      `json:"type" binding:"required"`
	At       interface{} `json:"at"`
	Msg      string      `json:"msg" binding:"required"`
	Resource string      `json:"resource"`
}

func Send(ctx *gin.Context) {
	var sr sendReq
	err := ctx.ShouldBindJSON(&sr)
	if err != nil {
		logs.Log.Errorf("获取请求参数失败:%s", err)
		ctx.JSON(200, gin.H{
			"code": 400,
			"msg":  "参数不合法",
		})
		return
	}
	if sr.AccessToken == config.ConfigValue.GuestToken {
		ctx.JSON(200, gin.H{
			"code": 401,
			"msg":  "请登录后体验完整功能!",
		})
		return
	}
	uid, err := middleware.GetUserId(sr.AccessToken)
	if err != nil {
		logs.Log.Errorf("用户信息获取失败:%s", err)
		ctx.JSON(200, gin.H{
			"code": 401,
			"msg":  "用户信息获取失败!",
		})
		return
	}
	u, err := GetUserData(uid)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code": 401,
			"msg":  "用户信息获取失败!",
		})
		return
	}
	r, err := dao.GetRoomById(sr.To)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "房间信息查询失败!",
		})
		return
	}
	savedPassword := ""
	if cachePassword, err := dao.GetCacheString(fmt.Sprintf("password_room_%d_password_%d", r.RoomId, uid)); err == nil {
		savedPassword = cachePassword
	}
	if r.RoomPublic == 1 && uid != r.RoomUser && !utils.GetIsAdmin(u.UserGroup) && savedPassword != r.RoomPassword {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "密码错误，发送失败!",
		})
		return
	}
	if r.RoomSendmsg == 1 && uid != r.RoomUser && !utils.GetIsAdmin(u.UserGroup) {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "全员禁言中,你暂时无法发言!",
		})
		return
	}
	if r.RoomSendmsg == 2 && uid != r.RoomUser && !utils.GetIsAdmin(u.UserGroup) && !IsVip(r.RoomId, uid) {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "抱歉,当前房间仅允许嘉宾发言!",
		})
		return
	}
	msg := strings.Replace(sr.Msg, " ", "", -1)
	if len(msg) == 0 {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "咱好歹说点啥吧？",
		})
		return
	}
	isban := false
	if _, err = dao.GetCacheString(fmt.Sprintf("shutdown_room_%d_user_%d", sr.To, uid)); err == nil {
		isban = true
	}
	switch sr.Where {
	case "channel":
		if isban && uid > 1 && utils.GetIsAdmin(u.UserGroup) {
			ctx.JSON(200, gin.H{
				"code": 500,
				"msg":  "你被房主禁止了发言权限!",
			})
			return
		}
	default:
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "未知的消息范围？",
		})
		return
	}
	if utils.GetIsAdmin(u.UserGroup) {
		if strings.Contains(sr.Msg, "@all") {
			sendAllMap := make(map[string]interface{})
			sendAllMap["type"] = "all"
			sendAllMap["content"] = strings.Replace(sr.Msg, "@all", "", -1)
			socket.SendMsgToAll(sendAllMap)
			ctx.JSON(200, gin.H{
				"code": 200,
				"msg":  "已发送",
			})
			return
		}
	} else {
		ip := ctx.Request.RemoteAddr
		if _, err = dao.GetCacheString("black_ip_" + ip); err == nil {
			ctx.JSON(200, gin.H{
				"code": 500,
				"msg":  "你所在的IP地址(" + ip + ")被Ban,你无法发送消息。",
			})
			return
		}
		switch sr.Type {
		case "text":
			if uid != r.RoomUser {
				if len(sr.Msg) > 250 {
					ctx.JSON(200, gin.H{
						"code": 500,
						"msg":  "发送文字超过最大限制！",
					})
					return
				}
				if _, err = dao.GetCacheString(fmt.Sprintf("last_%d", uid)); err == nil {
					ctx.JSON(200, gin.H{
						"code": 500,
						"msg":  "发送消息太频繁啦~",
					})
					return
				}
				if oldMsg, err := dao.GetCacheString(fmt.Sprintf("message_%d", uid)); err == nil {
					if oldMsg == sr.Msg {
						ctx.JSON(200, gin.H{
							"code": 500,
							"msg":  "灌水可耻,请不要重复发送相同信息",
						})
						return
					}
				}
			}
		case "img":
			if !strings.Contains(sr.Msg, sr.Resource) {
				ctx.JSON(200, gin.H{
					"code": 500,
					"msg":  "图片发送失败,我怀疑你在搞事情",
				})
				return
			}
			if strings.Contains(sr.Msg, "https://") || strings.Contains(sr.Msg, "http://") {
				if !strings.Contains(sr.Msg, config.ConfigValue.StorePath) && !strings.Contains(sr.Msg, "new/images") {
					ctx.JSON(200, gin.H{
						"code": 500,
						"msg":  "暂不支持站外图",
					})
					return
				}
			}
		}
	}
	msgUrl, _ := url.QueryUnescape(sr.Msg)
	switch sr.Type {
	case "text":
		// 机票
		if strings.Contains(msgUrl, config.ConfigValue.FrontendUrl) {
			regUrl := regexp.MustCompile(`/(\d+)`)
			rids := regUrl.FindAllStringSubmatch(msgUrl, -1)
			rid := 0
			if len(rids) > 0 {
				ridStr := rids[len(rids)-1][1]
				rid, _ = strconv.Atoi(ridStr)
			}
			if jumpRoom, err := dao.GetRoomById(rid); err == nil {
				_, err := dao.GetCacheString(fmt.Sprintf("chat_message_jump_%d", uid))
				if err == nil && !utils.GetIsAdmin(u.UserGroup) {
					ctx.JSON(200, gin.H{
						"code": 500,
						"msg":  "发送机票过于频繁",
					})
					return
				} else {
					err = dao.SetCacheString(fmt.Sprintf("chat_message_jump_%d", uid), true, 60*time.Second)
					if err != nil {
						logs.Log.Errorf("缓存机票信息失败:%s", err)
					}
					if jumpRoom.RoomPassword == "" {
						jumpRoom.RoomPassword = "true"
					} else {
						jumpRoom.RoomPassword = "false"
					}
					m := dao.Message{
						MessageUser:    uid,
						MessageType:    "text",
						MessageContent: sr.Msg,
						MessageStatus:  1,
						MessageWhere:   sr.Where,
						MessageTo:      strconv.Itoa(r.RoomId),
					}
					m, err = dao.CreateMessage(m)
					if err != nil {
						ctx.JSON(200, gin.H{
							"code": 500,
							"msg":  "发送机票失败",
						})
						return
					}
					sendMsg := make(map[string]interface{})
					sendMsg["type"] = "jump"
					sendMsg["jump"] = jumpRoom
					sendMsg["message_id"] = m.MessageId
					sendMsg["message_time"] = time.Now().Unix()
					sendMsg["user"] = u
					socket.SendMsgToRoom(r.RoomId, sendMsg)
					contentMap := make(map[string]interface{})
					contentMap["type"] = "text"
					contentMap["where"] = sr.Where
					contentMap["at"] = sr.At
					contentMap["message_id"] = m.MessageId
					contentMap["message_time"] = time.Now().Unix()
					contentMap["content"] = sr.Msg
					contentMap["resource"] = sr.Resource
					contentMap["user"] = u
					updateMessage := make(map[string]interface{})
					updateMessage["message_type"] = "text"
					if contentByte, err := json.Marshal(contentMap); err == nil {
						updateMessage["message_content"] = string(contentByte)
					}
					updateMessage["message_status"] = 0
					err = dao.UpdateMessageById(m.MessageId, updateMessage)
					if err != nil {
						logs.Log.Errorf("发送机票时更新数据库失败:%s", err)
					}
					ctx.JSON(200, gin.H{
						"code": 200,
						"msg":  "发送机票成功",
					})
					return
				}
			}
		}
		// 链接
		// 关键词替换

		m := dao.Message{
			MessageUser:    uid,
			MessageType:    "text",
			MessageContent: sr.Msg,
			MessageStatus:  1,
			MessageWhere:   sr.Where,
			MessageTo:      strconv.Itoa(r.RoomId),
		}
		m, err = dao.CreateMessage(m)
		if err != nil {
			ctx.JSON(200, gin.H{
				"code": 500,
				"msg":  "发送消息失败",
			})
			return
		}
		sendMsg := make(map[string]interface{})
		sendMsg["type"] = "text"
		sendMsg["where"] = sr.Where
		sendMsg["resource"] = sr.Resource
		sendMsg["at"] = sr.At
		sendMsg["content"] = sr.Msg
		sendMsg["message_id"] = m.MessageId
		sendMsg["message_time"] = time.Now().Unix()
		sendMsg["user"] = u
		socket.SendMsgToRoom(r.RoomId, sendMsg)
		contentMap := make(map[string]interface{})
		contentMap["type"] = "text"
		contentMap["where"] = sr.Where
		contentMap["at"] = sr.At
		contentMap["message_id"] = m.MessageId
		contentMap["message_time"] = time.Now().Unix()
		contentMap["content"] = sr.Msg
		contentMap["resource"] = sr.Resource
		contentMap["user"] = u
		updateMessage := make(map[string]interface{})
		updateMessage["message_type"] = "text"
		if contentByte, err := json.Marshal(contentMap); err == nil {
			updateMessage["message_content"] = string(contentByte)
		}
		updateMessage["message_status"] = 0
		err = dao.UpdateMessageById(m.MessageId, updateMessage)
		if err != nil {
			logs.Log.Errorf("发送消息时更新数据库失败:%s", err)
		}
		err = dao.SetCacheString(fmt.Sprintf("last_%d", uid), 1, 2*time.Second)
		if err != nil {
			logs.Log.Errorf("缓存发送消息失败:%s", err)
		}
		err = dao.SetCacheString(fmt.Sprintf("message_%d", uid), msgUrl, 10*time.Second)
		if err != nil {
			logs.Log.Errorf("缓存发送消息失败:%s", err)
		}
		ctx.JSON(200, gin.H{
			"code": 200,
			"msg":  "发送消息成功",
		})
		// 机器人
		ifRobotEnable := false
		switch sr.At.(type) {
		case map[string]interface{}:
			if _, err = dao.GetCacheString(fmt.Sprintf("shutdown_room_%d_user_1", r.RoomId)); err != nil {
				if id, ok := sr.At.(map[string]interface{})["user_id"]; ok {
					if id == 1.0 {
						rand2.Seed(time.Now().UnixMilli())
						if rand2.Int31n(999999) < 900000 {
							ifRobotEnable = true
						}
					}
				}
			}
		default:
			rand2.Seed(time.Now().UnixMilli())
			if _, err = dao.GetCacheString(fmt.Sprintf("shutdown_room_%d_user_1", r.RoomId)); err != nil && rand2.Int31n(999999)%10 == 0 {
				ifRobotEnable = true
			}
		}
		if ifRobotEnable && config.ConfigValue.TencentAiToken != "" {
			signUrl := "https://openai.weixin.qq.com/openapi/sign/" + config.ConfigValue.TencentAiToken
			postData := make(map[string]interface{})
			postData["userid"] = uid
			postData["username"] = u.UserName
			postDataByte, err := json.Marshal(postData)
			if err != nil {
				logs.Log.Errorf("设置机器人登录请求参数失败:%s", err)
				break
			}
			resp, err := http.Post(signUrl, "application/json", bytes.NewReader(postDataByte))
			if err != nil {
				logs.Log.Errorf("请求机器人登录参数失败:%s", err)
				break
			}
			signatureMap := make(map[string]interface{})
			defer resp.Body.Close()
			respByte, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				logs.Log.Errorf("读取机器人登录参数失败:%s", err)
				break
			}
			err = json.Unmarshal(respByte, &signatureMap)
			if err != nil {
				logs.Log.Errorf("解析机器人登录参数失败:%s", err)
				break
			}
			signature := ""
			if s, ok := signatureMap["signature"]; ok {
				signature = s.(string)
			} else {
				logs.Log.Error("未获取到机器人登录用户")
				break
			}
			postData["signature"] = signature
			postData["query"] = msgUrl
			postDataByte, err = json.Marshal(postData)
			if err != nil {
				logs.Log.Errorf("设置机器人请求参数失败:%s", err)
				break
			}
			urlAi := "https://openai.weixin.qq.com/openapi/aibot/" + config.ConfigValue.TencentAiToken
			respAi, err := http.Post(urlAi, "application/json", bytes.NewReader(postDataByte))
			if err != nil {
				logs.Log.Errorf("请求机器人消息失败:%s", err)
				break
			}
			aiMap := make(map[string]interface{})
			defer resp.Body.Close()
			aiByte, err := ioutil.ReadAll(respAi.Body)
			if err != nil {
				logs.Log.Errorf("读取机器人消息失败:%s", err)
				break
			}
			err = json.Unmarshal(aiByte, &aiMap)
			if err != nil {
				logs.Log.Errorf("解析机器人消息失败:%s", err)
				break
			}
			if t, ok := aiMap["answer_type"]; ok && t == "text" {
				aiAnswer := ""
				if a, ok := aiMap["answer"]; ok {
					aiAnswer = a.(string)
				} else {
					logs.Log.Error("机器人无回答")
					break
				}
				robot, err := GetUserData(1)
				if err != nil {
					logs.Log.Errorf("获取机器人用户失败:%s", err)
					break
				}
				at := make(map[string]interface{})
				at["user_id"] = uid
				at["user_name"] = u.UserName
				aiSendMap := make(map[string]interface{})
				aiSendMap["type"] = "text"
				aiSendMap["where"] = sr.Where
				aiSendMap["resource"] = aiAnswer
				aiSendMap["content"] = aiAnswer
				aiSendMap["at"] = at
				aiSendMap["message_id"] = 0
				aiSendMap["message_time"] = time.Now().Unix()
				aiSendMap["user"] = robot
				socket.SendMsgToRoom(r.RoomId, aiSendMap)
				m := dao.Message{
					MessageUser:    1,
					MessageType:    "text",
					MessageContent: aiAnswer,
					MessageStatus:  1,
					MessageWhere:   sr.Where,
					MessageTo:      strconv.Itoa(r.RoomId),
				}
				m, err = dao.CreateMessage(m)
				if err != nil {
					logs.Log.Errorf("存储机器人信息失败:%s", err)
				}
				contentMap = make(map[string]interface{})
				contentMap["type"] = "text"
				contentMap["where"] = sr.Where
				contentMap["at"] = at
				contentMap["message_id"] = m.MessageId
				contentMap["message_time"] = time.Now().Unix()
				contentMap["content"] = aiAnswer
				contentMap["resource"] = aiAnswer
				contentMap["user"] = robot
				updateMessage = make(map[string]interface{})
				updateMessage["message_type"] = "text"
				if contentByte, err := json.Marshal(contentMap); err == nil {
					updateMessage["message_content"] = string(contentByte)
				}
				updateMessage["message_status"] = 0
				err = dao.UpdateMessageById(m.MessageId, updateMessage)
				if err != nil {
					logs.Log.Errorf("发送消息时更新数据库失败:%s", err)
				}
			}
		}
		return
	case "img":
		if _, err = dao.GetCacheString(fmt.Sprintf("last_%d", uid)); err == nil && !utils.GetIsAdmin(u.UserGroup) && uid != r.RoomUser {
			ctx.JSON(200, gin.H{
				"code": 500,
				"msg":  "发送图片太频繁啦~",
			})
			return
		}
		if msgCache, err := dao.GetCacheString(fmt.Sprintf("last_%d", uid)); err == nil && msgCache == msgUrl && !utils.GetIsAdmin(u.UserGroup) && uid != r.RoomUser {
			ctx.JSON(200, gin.H{
				"code": 500,
				"msg":  "请不要连续发送相同的图片~",
			})
			return
		}
		m := dao.Message{
			MessageUser:    uid,
			MessageType:    "text",
			MessageContent: sr.Msg,
			MessageStatus:  1,
			MessageWhere:   sr.Where,
			MessageTo:      strconv.Itoa(r.RoomId),
		}
		m, err = dao.CreateMessage(m)
		if err != nil {
			ctx.JSON(200, gin.H{
				"code": 500,
				"msg":  "发送图片失败",
			})
			return
		}
		sendMsg := make(map[string]interface{})
		sendMsg["type"] = "img"
		sendMsg["where"] = sr.Where
		sendMsg["resource"] = msgUrl
		sendMsg["at"] = sr.At
		sendMsg["content"] = msgUrl
		sendMsg["message_id"] = m.MessageId
		sendMsg["message_time"] = time.Now().Unix()
		sendMsg["user"] = u
		socket.SendMsgToRoom(r.RoomId, sendMsg)
		contentMap := make(map[string]interface{})
		contentMap["type"] = "img"
		contentMap["where"] = sr.Where
		contentMap["at"] = sr.At
		contentMap["message_id"] = m.MessageId
		contentMap["message_time"] = time.Now().Unix()
		contentMap["content"] = msgUrl
		contentMap["resource"] = msgUrl
		contentMap["user"] = u
		updateMessage := make(map[string]interface{})
		updateMessage["message_type"] = "img"
		if contentByte, err := json.Marshal(contentMap); err == nil {
			updateMessage["message_content"] = string(contentByte)
		}
		updateMessage["message_status"] = 0
		err = dao.UpdateMessageById(m.MessageId, updateMessage)
		if err != nil {
			logs.Log.Errorf("发送图片时更新数据库失败:%s", err)
		}
		err = dao.SetCacheString(fmt.Sprintf("last_%d", uid), 1, 2*time.Second)
		if err != nil {
			logs.Log.Errorf("缓存发送图片失败:%s", err)
		}
		err = dao.SetCacheString(fmt.Sprintf("message_%d", uid), msgUrl, 10*time.Second)
		if err != nil {
			logs.Log.Errorf("缓存发送图片路径失败:%s", err)
		}
		ctx.JSON(200, gin.H{
			"code": 200,
			"msg":  "发送图片成功",
		})
		return
	default:
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "未知的消息类型",
		})
		return
	}
}

type msgListReq struct {
	BasicReq
	RoomId       int    `json:"room_id" binding:"required"`
	MessageWhere string `json:"message_where"`
	Page         int    `json:"page"`
	PerPage      int    `json:"per_page"`
}

func GetMessageList(ctx *gin.Context) {
	var mlr msgListReq
	err := ctx.ShouldBindJSON(&mlr)
	if err != nil {
		logs.Log.Errorf("获取请求参数失败:%s", err)
		ctx.JSON(200, gin.H{
			"code": 400,
			"msg":  "参数不合法",
		})
		return
	}
	if mlr.MessageWhere == "" {
		mlr.MessageWhere = "channel"
	}
	r, err := dao.GetRoomById(mlr.RoomId)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "房间信息查询失败!",
		})
		return
	}
	if mlr.Page == 0 {
		mlr.Page = 1
	}
	if mlr.PerPage == 0 {
		mlr.PerPage = 100
	}
	if mlr.PerPage > 100 {
		mlr.PerPage = 100
	}
	if mlr.PerPage < 1 {
		mlr.PerPage = 1
	}
	roomMsgListCachName := fmt.Sprintf("room_message_list_%d", mlr.RoomId)
	var mls []dao.Message
	if mlr.AccessToken == config.ConfigValue.GuestToken {
		if r.RoomPublic == 1 {
			ctx.JSON(200, gin.H{
				"code": 200,
				"msg":  "加密房间!",
				"data": mls,
			})
			return
		}
		mlsStr, err := dao.GetCacheString(roomMsgListCachName)
		if err != nil {
			mlsMap := make(map[string]interface{})
			mlsMap["message_to"] = strconv.Itoa(r.RoomId)
			mlsMap["message_where"] = mlr.MessageWhere
			mlsMap["message_status"] = 0
			mls = dao.GetMessageListByMap(mlsMap, mlr.Page, mlr.PerPage)
			mlsByte, err := json.Marshal(mls)
			if err != nil {
				logs.Log.Errorf("编码游客获取的消息失败:%s", err)
			}
			err = dao.SetCacheString(roomMsgListCachName, string(mlsByte), 10*time.Second)
			if err != nil {
				logs.Log.Errorf("缓存游客获取的消息失败:%s", err)
			}
			ctx.JSON(200, gin.H{
				"code": 200,
				"msg":  "获取成功!",
				"data": mls,
			})
			return
		} else {
			err = json.Unmarshal([]byte(mlsStr), &mls)
			if err != nil {
				logs.Log.Errorf("从缓存中解码游客获取的消息失败:%s", err)
				mlsMap := make(map[string]interface{})
				mlsMap["message_to"] = strconv.Itoa(r.RoomId)
				mlsMap["message_where"] = mlr.MessageWhere
				mlsMap["message_status"] = 0
				mls = dao.GetMessageListByMap(mlsMap, mlr.Page, mlr.PerPage)
				mlsByte, err := json.Marshal(mls)
				if err != nil {
					logs.Log.Errorf("编码游客获取的消息失败:%s", err)
				}
				err = dao.SetCacheString(roomMsgListCachName, string(mlsByte), 10*time.Second)
				if err != nil {
					logs.Log.Errorf("缓存游客获取的消息失败:%s", err)
				}
			}
			ctx.JSON(200, gin.H{
				"code": 200,
				"msg":  "获取成功!",
				"data": mls,
			})
			return
		}
	}
	uid, err := middleware.GetUserId(mlr.AccessToken)
	if err != nil {
		logs.Log.Errorf("用户信息获取失败:%s", err)
		ctx.JSON(200, gin.H{
			"code": 401,
			"msg":  "用户信息获取失败!",
		})
		return
	}
	u, err := GetUserData(uid)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code": 401,
			"msg":  "用户信息获取失败!",
		})
		return
	}
	savedPassword := ""
	if cachePassword, err := dao.GetCacheString(fmt.Sprintf("password_room_%d_password_%d", mlr.RoomId, uid)); err == nil {
		savedPassword = cachePassword
	}
	if r.RoomPublic == 1 && uid != r.RoomUser && !utils.GetIsAdmin(u.UserGroup) && savedPassword != r.RoomPassword {
		ctx.JSON(200, gin.H{
			"code": 200,
			"msg":  "加密房间!",
			"data": mls,
		})
		return
	}
	userMsgCacheName := fmt.Sprintf("room_message_user_%d_list_%d", uid, mlr.Page)
	mlsStr, err := dao.GetCacheString(userMsgCacheName)
	if err != nil {
		mlsMap := make(map[string]interface{})
		mlsMap["message_to"] = strconv.Itoa(r.RoomId)
		mlsMap["message_where"] = mlr.MessageWhere
		mlsMap["message_status"] = 0
		mls = dao.GetMessageListByMap(mlsMap, mlr.Page, mlr.PerPage)
		mlsByte, err := json.Marshal(mls)
		if err != nil {
			logs.Log.Errorf("编码用户获取的消息失败:%s", err)
		}
		err = dao.SetCacheString(userMsgCacheName, string(mlsByte), 10*time.Second)
		if err != nil {
			logs.Log.Errorf("缓存用户获取的消息失败:%s", err)
		}
		ctx.JSON(200, gin.H{
			"code": 200,
			"msg":  "获取成功!",
			"data": mls,
		})
		return
	} else {
		err = json.Unmarshal([]byte(mlsStr), &mls)
		if err != nil {
			logs.Log.Errorf("从缓存中解码用户获取的消息失败:%s", err)
			mlsMap := make(map[string]interface{})
			mlsMap["message_to"] = strconv.Itoa(r.RoomId)
			mlsMap["message_where"] = mlr.MessageWhere
			mlsMap["message_status"] = 0
			mls = dao.GetMessageListByMap(mlsMap, mlr.Page, mlr.PerPage)
			mlsByte, err := json.Marshal(mls)
			if err != nil {
				logs.Log.Errorf("编码用户获取的消息失败:%s", err)
			}
			err = dao.SetCacheString(userMsgCacheName, string(mlsByte), 10*time.Second)
			if err != nil {
				logs.Log.Errorf("缓存用户获取的消息失败:%s", err)
			}
		}
		ctx.JSON(200, gin.H{
			"code": 200,
			"msg":  "获取成功!",
			"data": mls,
		})
		return
	}
}

type clearMsgReq struct {
	BasicReq
	RoomId int `json:"room_id" binding:"required"`
}

func ClearMessage(ctx *gin.Context) {
	var cmr clearMsgReq
	err := ctx.ShouldBindJSON(&cmr)
	if err != nil {
		logs.Log.Errorf("获取请求参数失败:%s", err)
		ctx.JSON(200, gin.H{
			"code": 400,
			"msg":  "参数不合法",
		})
		return
	}
	if cmr.AccessToken == config.ConfigValue.GuestToken {
		ctx.JSON(200, gin.H{
			"code": 401,
			"msg":  "请登录后体验完整功能!",
		})
		return
	}
	r, err := dao.GetRoomById(cmr.RoomId)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "房间信息查询失败!",
		})
		return
	}
	uid, err := middleware.GetUserId(cmr.AccessToken)
	if err != nil {
		logs.Log.Errorf("用户信息获取失败:%s", err)
		ctx.JSON(200, gin.H{
			"code": 401,
			"msg":  "用户信息获取失败!",
		})
		return
	}
	u, err := GetUserData(uid)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code": 401,
			"msg":  "用户信息获取失败!",
		})
		return
	}
	if !utils.GetIsAdmin(u.UserGroup) && r.RoomUser != uid {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "你没有权限删除房间聊天记录!",
		})
		return
	}
	err = dao.DeleteMessageByRid(strconv.Itoa(r.RoomId))
	if err != nil {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "清空聊天记录失败!",
		})
		return
	}
	sendMsg := make(map[string]interface{})
	sendMsg["user"] = u
	sendMsg["type"] = "clear"
	sendMsg["time"] = time.Now().Format("15:04:05")
	socket.SendMsgToRoom(r.RoomId, sendMsg)
	ctx.JSON(200, gin.H{
		"code": 200,
		"msg":  "清空聊天记录成功!",
	})
	return
}

type backMsgReq struct {
	BasicReq
	RoomId    int   `json:"room_id" binding:"required"`
	MessageId int64 `json:"message_id" binding:"required"`
}

func BackMessage(ctx *gin.Context) {
	var bmr backMsgReq
	err := ctx.ShouldBindJSON(&bmr)
	if err != nil {
		logs.Log.Errorf("获取请求参数失败:%s", err)
		ctx.JSON(200, gin.H{
			"code": 400,
			"msg":  "参数不合法",
		})
		return
	}
	if bmr.AccessToken == config.ConfigValue.GuestToken {
		ctx.JSON(200, gin.H{
			"code": 401,
			"msg":  "请登录后体验完整功能!",
		})
		return
	}
	r, err := dao.GetRoomById(bmr.RoomId)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "房间信息查询失败!",
		})
		return
	}
	m, err := dao.GetMessageById(bmr.MessageId)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "消息查询失败,无法撤回!",
		})
		return
	}
	if m.MessageTo != strconv.Itoa(bmr.RoomId) && m.MessageWhere == "channel" {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "消息与房间信息不匹配",
		})
		return
	}
	uid, err := middleware.GetUserId(bmr.AccessToken)
	if err != nil {
		logs.Log.Errorf("用户信息获取失败:%s", err)
		ctx.JSON(200, gin.H{
			"code": 401,
			"msg":  "用户信息获取失败!",
		})
		return
	}
	u, err := GetUserData(uid)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code": 401,
			"msg":  "用户信息获取失败!",
		})
		return
	}
	if !utils.GetIsAdmin(u.UserGroup) && r.RoomUser != uid && m.MessageUser != uid {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "你没有权限撤回该消息!",
		})
		return
	}
	if time.Now().Unix() > m.MessageCreatetime+300 && !utils.GetIsAdmin(u.UserGroup) {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "你只能撤回5分钟内的消息!",
		})
		return
	}
	err = dao.DeleteMessageById(m.MessageId)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "撤回消息失败!",
		})
		return
	}
	sendMsg := make(map[string]interface{})
	sendMsg["user"] = u
	sendMsg["type"] = "back"
	sendMsg["message_id"] = m.MessageId
	sendMsg["time"] = time.Now().Format("15:04:05")
	socket.SendMsgToRoom(r.RoomId, sendMsg)
	ctx.JSON(200, gin.H{
		"code": 200,
		"msg":  "撤回消息成功!",
	})
	return
}

type touchReq struct {
	BasicReq
	RoomId int `json:"room_id" binding:"required"`
	At     int `json:"at" binding:"required"`
}

func Touch(ctx *gin.Context) {
	var tr touchReq
	err := ctx.ShouldBindJSON(&tr)
	if err != nil {
		logs.Log.Errorf("获取请求参数失败:%s", err)
		ctx.JSON(200, gin.H{
			"code": 400,
			"msg":  "参数不合法",
		})
		return
	}
	if tr.AccessToken == config.ConfigValue.GuestToken {
		ctx.JSON(200, gin.H{
			"code": 401,
			"msg":  "请登录后体验完整功能!",
		})
		return
	}
	r, err := dao.GetRoomById(tr.RoomId)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "房间信息查询失败!",
		})
		return
	}
	atUser, err := GetUserData(tr.At)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "被摸的人信息查询失败",
		})
		return
	}

	uid, err := middleware.GetUserId(tr.AccessToken)
	if err != nil {
		logs.Log.Errorf("用户信息获取失败:%s", err)
		ctx.JSON(200, gin.H{
			"code": 401,
			"msg":  "用户信息获取失败!",
		})
		return
	}
	if atUser.UserId == uid {
		ctx.JSON(200, gin.H{
			"code": 500,
			"msg":  "“自己摸自己是一种什么样的感觉？”——佚名",
		})
		return
	}
	u, err := GetUserData(uid)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code": 401,
			"msg":  "用户信息获取失败!",
		})
		return
	}
	touchCDTime := 60
	tochCachName := fmt.Sprintf("touch_%d_%d", r.RoomId, uid)
	if !utils.GetIsAdmin(u.UserGroup) && r.RoomUser != uid {
		if tochTime, err := dao.GetCacheString(tochCachName); err == nil {
			touchTimeCache, _ := strconv.ParseInt(tochTime, 10, 64)
			needTime := int64(touchCDTime) - (time.Now().Unix() - touchTimeCache)
			if needTime > 0 {
				ctx.JSON(200, gin.H{
					"code": 500,
					"msg":  fmt.Sprintf("你摸得太频繁啦，请%ds后再试", needTime),
				})
				return
			}
		}
	}
	err = dao.SetCacheString(tochCachName, time.Now().Unix(), 0)
	if err != nil {
		logs.Log.Errorf("缓存用户摸一摸失败:%s", err)
	}
	sendMsg := make(map[string]interface{})
	sendMsg["user"] = u
	sendMsg["type"] = "touch"
	sendMsg["at"] = atUser
	sendMsg["time"] = time.Now().Format("15:04:05")
	socket.SendMsgToRoom(r.RoomId, sendMsg)
	ctx.JSON(200, gin.H{
		"code": 200,
		"msg":  "操作成功!",
	})

	// 彩蛋区域
	robot, err := GetUserData(1)
	if err != nil {
		logs.Log.Errorf("获取机器人用户失败:%s", err)
		return
	}
	if _, err := dao.GetCacheString(fmt.Sprintf("shutdown_room_%d_user_1", r.RoomId)); err == nil {
		return
	}
	rand2.Seed(time.Now().UnixMilli())
	rand := rand2.Int63n(999999)
	if rand < 800000 && tr.At == 1 {
		randStrs := []string{"再摸小心我给你多戴几顶绿帽子！",
			"摸摸摸整天就知道摸摸摸，再摸我要生气了",
			"摸锤子摸，没见过机器人啊？",
			"别摸了~啊~受不了了~~~",
			"再摸试试，给你两哈",
			"别摸了，赶紧点歌去吧。。。",
			"莫挨劳资"}
		rand2.Seed(time.Now().UnixMilli())
		content := randStrs[rand2.Int31n(int32(len(randStrs))-1)]
		atMap := make(map[string]interface{})
		atMap["user_id"] = uid
		atMap["user_name"] = u.UserName
		sendMsg := make(map[string]interface{})
		sendMsg["user"] = robot
		sendMsg["type"] = "text"
		sendMsg["at"] = atMap
		sendMsg["message_id"] = 0
		sendMsg["message_time"] = time.Now().Unix()
		sendMsg["resource"] = content
		sendMsg["content"] = content
		socket.SendMsgToRoom(r.RoomId, sendMsg)
	}
	return
}
